#include <iostream>

using namespace std;

class Test {
public:
    int *sum;
    int x;
    int y;

    Test() {
        cout << "调用了Test类的无参构造函数" << endl;
        x = 0;
        y = 0;
        sum = new int[4];
    }

    //拷贝构造函数
    //语法(原型): 类名(const 类名 &变量名);
    //调用时机: 使用一个构造好的对象初始化一个新的对象时
    //> 思考：为什么拷贝构造函数的形参为引用？
    //  1、如果函数的形参为普通对象，那么调用函数时形参对象会被构造，函数调用结束形参对象还需要被销毁
    //  2、为了避免形参对象这种"临时对象"的创建，故而将形参设计成引用(在实际工作中也可以引用上)
    //> 思考：为什么拷贝构造函数的形参需要使用const修饰？
    //  如果我们不需要在函数中修改引用的对象可以使用const修饰形参
    Test(const Test &t) //t 引用的是 右值
    {
        cout << "调用了Test类的拷贝构造函数" << endl;
        //将t对象中的成员变量的值拷贝给新的对象
        x = t.x;
        y = t.y;
        sum = new int[4];//为新的对象的指针变量申请堆空间
        memcpy(sum, t.sum, 4 * sizeof(int));//将右值对象指针所指向的空间中的内容拷贝到新的对象指针所指向的堆空间中。
    }


    Test(int a, int b) : x(a), y(b) {
        cout << "调用了Test类的有参构造函数" << endl;
        sum = new int[4];
    }

    ~Test() {
        cout << "调用了Test类的析构函数" << endl;
        delete[] sum;
    }
};

//错误地使用一个已经构造好的对象去初始化另一个新的对象存在的问题(测试时需要将拷贝构造函数注释掉再使用，并且必然会报错):
void errorFunc() {
    Test *t1 = new Test(10, 20); //调用构造函数： Test(int a, int b) : x(a), y(b)
    //使用一个已经构造好的对象去初始化另一个新的对象，C++编译器是如何处理这个操作的呢？
    Test t2 = *t1;
    cout << "t2.x: " << t2.x << endl;
    cout << "t2.y: " << t2.y << endl;
    //t1.sum的值和t2.sum的值相等，意味着t1.sum 和t2.sum指向了同一块内存空间。这意味着对t1.sum里面的值进行修改，t2.sum也会受到影响
    cout << "t1.sum: " << t1->sum << endl;
    cout << "t2.sum: " << t2.sum << endl;
    cout << "t2.sum[0]: " << t2.sum[0] << endl;
    t1->sum[0] = 100;
    cout << "t2.sum[0]: " << t2.sum[0] << endl;
    t1->sum[1] = 101;
    t1->sum[2] = 102;
    t1->sum[3] = 103;
    //手动释放t1指着所申请的堆空间，意味着销毁掉 *t1这个对象，会自动调用析构函数，顺带手动释放成员变量sum所申请的堆空间
    delete t1;
    cout << "t2.sum: " << t2.sum << endl;
    //值不一样了，因为在t1->sum 和 t2->sum指向的是同一块内存空间，当执行delete t1语句时会自动调用析构函数，
    // 在析构函数中delete[] this->sum，将原来在堆山申请的空间释放了，所以t2->sum[1]的值就不是原来的值了。
    cout << t2.sum[0] << endl;
    cout << t2.sum[1] << endl;
    cout << t2.sum[2] << endl;
    cout << t2.sum[3] << endl;
    //而更严重的问题是，在销毁t2对象时会再次调用析构函数再次执行delete[] sum，因为sum已经被释放了，则会造成进程异常-1073740940 (0xC0000374)
    /*
        结论:
        用一个构造好的对象使用=可以构造一个新的对象，而且新的对象中的成员变量的值和构造好的对象中的成员变量的值是相等的（实现对象的拷贝）。
        但是这样做带来的危害是：如果两个类中有成员变量是指针类型，一旦其中一个对象被销毁调用析构函数将指针变量指向的堆空间进行了释放，
        另外一个对象所操作的指针所执行的堆空间是非法的！！
     */
}

int main() {

//    errorFunc();

    cout << "解决方法1:乖乖自己一点点赋值" << endl;
    Test t1(10, 20); //调用构造函数： Test(int a, int b) : x(a), y(b)
    t1.sum[0] = 100;
    t1.sum[1] = 101;
    t1.sum[2] = 102;
    t1.sum[3] = 103;
    Test t2;
    t2.x = t1.x;
    t2.y = t2.y;
    // for (int i = 0; i < 4; i++)
    //     t2.sum[i] = t1.sum[i];
    //或者:
    memcpy(t2.sum, t1.sum, 4 * sizeof(int));
    cout << t1.sum << endl;
    cout << t2.sum << endl;
    for (int i = 0; i < 4; i++)
        cout << t2.sum[i] << endl;
    cout << endl;

    cout << "解决方法2:依然使用=(TIPS:实际上，当我们使用=用一个构造好的对象初始化一个新的对象时，会自动调用一个拷贝构造函数)" << endl;
    cout << t2.y << endl;
    //因为初始化一个新对象时才会调用，所以这样子是不会调用的(而是将t1赋值给了t2，则之后对t1的操作都相当于在操作t2)
    t2 = t1;
    cout << t2.y << endl;
    Test t3 = t1;
    cout << t1.sum[2] << " " << t2.sum[2] << " " << t3.sum[2] << endl;
    t1.sum[2] = 100;
    cout << t1.sum[2] << " " << t2.sum[2] << " " << t3.sum[2] << endl;

    /*
        深拷贝和浅拷贝:

        浅拷贝:
        1、同一类型的对象之间可以赋值，使得两个对象的成员变量的值相同，两个对象仍然是独立的两个对象，这种情况被称为"浅拷贝"
        2、一般情况下，浅拷贝没有任何副作用，但是当类中有指针，并且指针指向动态分配的内存空间，将导致两个对象的指针变量指向同一块内存空间，
        当两个对象被销毁时调用析构函数，因为在析构函数中会释放指针所指向的堆空间，造成同一块堆空间被释放两次从而导致程序运行出错。
        3、如果我们没有实现拷贝构造函数，C++编译器会自动实现一个拷贝构造函数，我们称之为默认拷贝构造函数，但是在默认拷贝构造函数中的实现是"浅拷贝"

        深拷贝:
        自行实现拷贝构造函数，在拷贝构造函数中需要对对象中的指针变量进行单独的内存申请。
        两个对象中的指针变量不会指向同一块内存空间，然后再将右值对象指针所指向的空间中的内容拷贝到新的对象指针所指向的堆空间中。
     */



    return 0;
}